package com.uuah.server.transport;

import javax.resource.spi.work.Work;

import com.uuah.api.work.IWorkManager;
import com.uuah.exception.AbstractUuahException;
import com.uuah.exception.lifecycle.InitialisationException;
import com.uuah.server.api.IUuahEvent;
import com.uuah.server.api.IUuahMessage;
import com.uuah.server.api.endpoint.IUuahEndpoint;
import com.uuah.server.api.transport.IMessageDispatcher;
import com.uuah.server.config.Constants;
import com.uuah.server.exception.transport.DispatchException;

/**
 * Provide a default dispatch (client) support for handling threads lifecycle
 * and validation..
 *
 * <p>update record:updator，update time，update content and version</p>
 * @author <a href="jonny_quan@hotmail.com">jonny</a>
 * @date 2009 2009-3-27 下午02:50:04
 * @version 1.0.0
 */
public abstract class AbstractMessageDispatcher extends AbstractConnectable
		implements IMessageDispatcher {
	public AbstractMessageDispatcher(IUuahEndpoint endpoint) {
		super(endpoint);
	}

	// @Override
	public final void initialise() throws InitialisationException {
		super.initialise();

		doInitialise();
	}

	// @Override
	public final synchronized void dispose() {
		super.dispose();
		try {
			disposing.set(true);
			doDispose();
		} finally {
			disposed.set(true);
		}
	}

	public final void dispatch(IUuahEvent event) throws DispatchException {
		event.setSynchronous(false);
		event.getMessage().setProperty(Constants.UUAH_ENDPOINT_PROPERTY,
				event.getEndpoint().getEndpointURI().toString());
		event = RequestContextHelper.criticalSetEvent(event);

		try {
			Worker worker = new Worker(event);
			if (isDoThreading() && !event.isSynchronous()) {
				connector.getDispatcherWorkManager().scheduleWork(
						new Worker(event), IWorkManager.INDEFINITE, null,
						connector);
			} else {
				// Execute within this thread
				worker.run();
			}
		} catch (Exception e) {
			handleException(new DispatchException(event.getMessage(), event
					.getEndpoint(), e));
		}
	}

	public final IUuahMessage send(IUuahEvent event) throws DispatchException {
		event.setSynchronous(true);
		event.getMessage().setProperty(Constants.UUAH_ENDPOINT_PROPERTY,
				event.getEndpoint().getEndpointURI().getUri().toString());
		event = RequestContextHelper.unsafeSetEvent(event);

		try {
			connect();
			IUuahMessage result = null;
			try {
				result = doSend(event);
			} finally {
				if (useStrictConnectDisconnect)
					disconnect();
			}

			return result;
		} catch (DispatchException e) {
			disposeAndLogException();
			throw e;
		} catch (Exception e) {
			disposeAndLogException();
			throw new DispatchException(event.getMessage(),
					event.getEndpoint(), e);
		}
	}

	private class Worker implements Work {
		private IUuahEvent event;

		public Worker(IUuahEvent event) {
			this.event = event;
		}

		public void run() {
			try {
				event = RequestContextHelper.setEvent(event);

				connect();
				try {
					doDispatch(event);
				} finally {
					if (useStrictConnectDisconnect)
						disconnect();
				}
			} catch (Exception e) {
				handleException(e);
			}
		}

		public void release() {
			// nothing to do
		}
	}

	// @Override
	protected IWorkManager getWorkManager() {
		try {
			return connector.getDispatcherWorkManager();
		} catch (AbstractUuahException e) {
			logger.error(e.getMessage());
			return null;
		}
	}

	protected abstract void doDispatch(IUuahEvent event) throws Exception;

	protected abstract IUuahMessage doSend(IUuahEvent event) throws Exception;
}
